/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.reporter;

import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.spi.LocationInfo;
import org.apache.log4j.spi.LoggingEvent;
import za.org.foobar.famon.dto.CLoggingEvent;

import java.util.ArrayList;
import java.util.LinkedList;

/**
 * 
 */
public class FamonAppender extends AppenderSkeleton {

    private int loggingEventsPerThread;
    private ThreadLocal<LinkedList<LoggingEvent>> loggingEventList = new ThreadLocal<LinkedList<LoggingEvent>>();

    private static final String packageName = FamonAppender.class.getPackage().getName();

    public FamonAppender() {
    }

    public int getLoggingEventsPerThread() {
        return loggingEventsPerThread;
    }

    public void setLoggingEventsPerThread(int loggingEventsPerThread) {
        this.loggingEventsPerThread = loggingEventsPerThread;
    }

    @Override
    public void activateOptions() {
        if (layout != null ) {
            errorHandler.error("The Fail appender does not use a layout");
        }
        if (loggingEventsPerThread < 0) {
            errorHandler.error("The CacheSize value must be a non-negative integer");
        }
    }

    protected void append(LoggingEvent loggingEvent) {
        if (loggingEventsPerThread <= 0) {
            return;
        }
        // Do not append our own logging events
        if (loggingEvent.getLoggerName().startsWith(packageName)) {
            return;
        }
        LinkedList<LoggingEvent> threadLoggingEvents = getThreadLoggingEvents();
        // Forcefully limit our maximum logging events to 50 in case this isn't setup properly
        int n = Math.min(loggingEventsPerThread, 50);
        while (threadLoggingEvents.size() >= n) {
            threadLoggingEvents.removeFirst();
        }
        threadLoggingEvents.addLast(loggingEvent);
        loggingEvent.getLocationInformation(); // It seems this information is lost if we don't request it now
    }

    public void close() {
    }

    public boolean requiresLayout() {
        return false;
    }

    private LinkedList<LoggingEvent> getThreadLoggingEvents() {
        LinkedList<LoggingEvent> threadCache = loggingEventList.get();
        if (threadCache == null) {
            loggingEventList.set(threadCache = new LinkedList<LoggingEvent>());
        }
        return threadCache;
    }

    public ArrayList<CLoggingEvent> getLoggingEvents() {
        LinkedList<LoggingEvent> threadLoggingEvents = getThreadLoggingEvents();
        ArrayList<CLoggingEvent> dtoList = new ArrayList<CLoggingEvent>(threadLoggingEvents.size());
        for (LoggingEvent e : threadLoggingEvents) {
            dtoList.add(getLoggingEventDto(e));
        }
        return dtoList;
    }

    public CLoggingEvent getLoggingEventDto(LoggingEvent ev) {
        LocationInfo info = ev.getLocationInformation();
        int lineNumber;
        try {
            lineNumber = Integer.parseInt(info.getLineNumber());
        } catch (NumberFormatException e) {
            lineNumber = 0;
        }
        return new CLoggingEvent(
            ev.getLoggerName(),
            ev.getLevel().toString(),
            ev.getMessage().toString(),
            info.getClassName(),
            info.getFileName(),
            info.getMethodName(),
            lineNumber
        );
    }


}
